package com.mall.cloud.service.fallback;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.mall.cloud.bean.User;
import com.mall.cloud.common.CommonResult;
import com.mall.cloud.service.UserHystrixService;
import com.netflix.hystrix.contrib.javanica.annotation.HystrixCollapser;
import com.netflix.hystrix.contrib.javanica.annotation.HystrixCommand;
import com.netflix.hystrix.contrib.javanica.annotation.HystrixProperty;
import com.netflix.hystrix.contrib.javanica.cache.annotation.CacheRemove;
import com.netflix.hystrix.contrib.javanica.cache.annotation.CacheResult;
import com.netflix.hystrix.contrib.javanica.command.AsyncResult;
import org.apache.commons.lang.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.client.RestTemplate;

import java.util.*;
import java.util.concurrent.Future;

@Service
public class UserHystrixServiceImpl implements UserHystrixService {
    private Logger LOGGER = LoggerFactory.getLogger(UserHystrixServiceImpl.class);
    @Autowired
    private RestTemplate restTemplate;
    @Value("${service-url.user-service}")
    private String userServiceUrl;

    @Override
    @HystrixCommand(fallbackMethod = "getDefaultUser")//服务降级，当前接口报错，返回getDefaultUser的方法信息。fallbackMethod：指定服务降级处理方法；
    public CommonResult getUser(Long id) {
        return restTemplate.getForObject(userServiceUrl + "/user/{1}", CommonResult.class, id);
    }

    private CommonResult getDefaultUser(@PathVariable Long id) {
        User defaultUser = new User(id, "defaultUser");
        return new CommonResult<>(defaultUser);
    }

    //commandKey：命令名称，用于区分不同的命令；
    //groupKey：分组名称，Hystrix会根据不同的分组来统计命令的告警及仪表盘信息；
    //threadPoolKey：线程池名称，用于划分线程池。
    @HystrixCommand(fallbackMethod = "getDefaultUser",
            commandKey = "getUserCommand",
            groupKey = "getUserGroup",
            threadPoolKey = "getUserThreadPool")
    public CommonResult getUserCommand(@PathVariable Long id) {
        return restTemplate.getForObject(userServiceUrl + "/user/{1}", CommonResult.class, id);
    }

    //ignoreExceptions：忽略某些异常，不发生服务降级；这里发生NullPointerException异常时不降级
    @Override
    @HystrixCommand(fallbackMethod = "getDefaultException",
            ignoreExceptions = {NullPointerException.class})
    public CommonResult getUserException(Long id) {
        if(id == 1) {
            throw new IndexOutOfBoundsException();
        }else if (id == 2) {
            throw new NullPointerException();
        }
        return restTemplate.getForObject(userServiceUrl + "/user/{1}", CommonResult.class, id);
    }

    private CommonResult getDefaultException(@PathVariable Long id, Throwable e) {
        LOGGER.error("getDefaultException id:{},throwable class:{}", id, e.getClass());
        User user = new User(id, "defaultException");
        return new CommonResult<>(user);
    }

    //@CacheResult：开启缓存，默认所有参数作为缓存的key，cacheKeyMethod可以通过返回String类型的方法指定key；
    //@CacheKey：指定缓存的key，可以指定参数或指定参数中的属性值为缓存key，cacheKeyMethod还可以通过返回String类型的方法指定；
    //@CacheRemove：移除缓存，需要指定commandKey。
    @Override
    //在缓存使用过程中，我们需要在每次使用缓存的请求前后对HystrixRequestContext进行初始化和关闭，否则会发生异常
    @CacheResult(cacheKeyMethod = "getCacheKey")
    @HystrixCommand(fallbackMethod = "getDefaultUser", commandKey = "getUserCache")
    public CommonResult getUserCache(Long id) {
        LOGGER.info("getUserCache id:{}", id);
        return restTemplate.getForObject(userServiceUrl + "/user/{1}", CommonResult.class, id);
    }

    @Override
    @CacheRemove(commandKey = "getUserCache", cacheKeyMethod = "getCacheKey")
    @HystrixCommand(fallbackMethod = "getDefaultUser")
    public CommonResult removeCache(Long id) {
        LOGGER.info("removeCache id:{}", id);
        return restTemplate.postForObject(userServiceUrl + "/user/delete/{1}", null, CommonResult.class, id);
    }

    //为缓存生成key的方法
    private String getCacheKey(Long id) {
        return String.valueOf(id);
    }

    @Override
    public CommonResult testList() {
        String users = restTemplate.getForObject(userServiceUrl + "/user/getUserByIds?ids=1,2,3", String.class);
        System.out.println(users);
        return new CommonResult(users);
    }

    @Override
    @HystrixCollapser(batchMethod = "getUserByIds", collapserProperties = {
            @HystrixProperty(name = "timerDelayInMilliseconds", value = "100")
    })
    public Future<User> getUserFuture(Long id) {
        return new AsyncResult<User>() {
            @Override
            public User invoke() {
                CommonResult commonResult = restTemplate.getForObject(userServiceUrl + "/user/{1}", CommonResult.class, id);
                User user = JSONObject.parseObject(JSON.toJSONString(commonResult.getData()), User.class);
                LOGGER.info("getUserById username:{}", user.getUsername());
                return user;
            }
        };
    }

    @Override
    @HystrixCommand
    public List<User> getUserByIds(List<Long> ids) {
        LOGGER.info("getUserByIds:{}", ids);
        CommonResult<List<Map>> commonResult = restTemplate.getForObject(userServiceUrl + "/user/getUserByIds?ids={1}", CommonResult.class, StringUtils.join(ids, ","));
        List<Map> data = commonResult.getData();
        List<User> users = new ArrayList<>();
        for(Map map : data) {
            User user = JSONObject.parseObject(JSON.toJSONString(map), User.class);
            users.add(user);
        }
        return users;
    }
}
